import os
import hashlib
import base64
import hmac
import requests
import urllib.parse
from datetime import datetime, timezone
import config

def generate_oss_signature(access_key_id, access_key_secret, method, content_type, resource, content_md5=""):
    """
    生成阿里云OSS RESTful API的签名

    Args:
        access_key_id: 访问密钥ID
        access_key_secret: 访问密钥密码
        method: HTTP方法（GET, PUT, POST等）
        content_type: 内容类型
        resource: OSS资源路径
        content_md5: 内容的MD5值（可选）

    Returns:
        tuple: (授权字符串, 日期字符串)
    """
    date = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')
    string_to_sign = f"{method}\n{content_md5}\n{content_type}\n{date}\n/{resource}"

    # 计算HMAC-SHA1签名
    h = hmac.new(
        access_key_secret.encode('utf-8'),
        string_to_sign.encode('utf-8'),
        hashlib.sha1
    )
    signature = base64.b64encode(h.digest()).decode('utf-8')

    # 构建授权字符串
    authorization = f"OSS {access_key_id}:{signature}"

    return authorization, date

def generate_signed_url(access_key_id, access_key_secret, bucket_name, host, object_name, expires_in_hours=1):
    """
    生成OSS签名URL（优化版）
    
    Args:
        access_key_id: 访问密钥ID
        access_key_secret: 访问密钥密码
        bucket_name: 存储桶名称
        host: OSS域名
        object_name: 对象名称
        expires_in_hours: 有效期（小时），默认1小时
        
    Returns:
        str: 签名URL
    """
    # 计算过期时间戳（秒）
    expires = int(datetime.now().timestamp()) + (expires_in_hours * 3600)
    
    # 构建要签名的资源字符串
    resource = f"/{bucket_name}/{object_name}"
    
    # 构建签名参数
    params = {
        'OSSAccessKeyId': access_key_id,
        'Expires': str(expires),
        'Signature': ''
    }
    
    # 构建要签名的字符串
    string_to_sign = f"GET\n\n\n{expires}\n{resource}"
    
    # 计算HMAC-SHA1签名
    h = hmac.new(
        access_key_secret.encode('utf-8'),
        string_to_sign.encode('utf-8'),
        hashlib.sha1
    )
    signature = base64.b64encode(h.digest()).decode('utf-8')
    
    # URL编码签名
    encoded_signature = urllib.parse.quote_plus(signature)
    
    # 构建最终的签名URL
    signed_url = f"https://{host}/{object_name}?OSSAccessKeyId={access_key_id}&Expires={expires}&Signature={encoded_signature}"
    
    return signed_url

def upload_to_oss(path):
    """
    使用RESTful API上传文件到阿里云OSS
    
    Args:
        path: 本地文件路径
        
    Returns:
        str: 上传后的文件URL（有效期1小时）
    """
    try:
        # 从配置文件获取OSS配置
        oss_config = config.Ali_OSS_CONFIG
        access_key_id = oss_config['access_key_id']
        access_key_secret = oss_config['access_key_secret']
        bucket_name = oss_config['bucket_name']
        endpoint = oss_config['endpoint']
        host = oss_config['host']
  
        file_name = os.path.basename(path)
        
        # 检查文件是否存在
        if not os.path.exists(path):
            raise FileNotFoundError(f"文件不存在: {path}")
        
        # 读取文件内容
        with open(path, 'rb') as f:
            file_content = f.read()
        
        # 生成签名
        content_type = "application/octet-stream"
        resource = f"{bucket_name}/{file_name}"
        authorization, date = generate_oss_signature(
            access_key_id, access_key_secret, "PUT", content_type, resource
        )

        # 构建请求URL和头部
        url = f"https://{host}/{file_name}"
        headers = {
            'Host': host,
            'Date': date,
            'Content-Type': content_type,
            'Content-Length': str(len(file_content)),
            'Authorization': authorization
        }

        # 发送PUT请求上传文件
        response = requests.put(url, headers=headers, data=file_content, timeout=30)
        
        # 检查上传是否成功
        if response.status_code == 200:
            # 生成有效期1小时的签名URL
            signed_url = generate_signed_url(
                access_key_id, access_key_secret, bucket_name, host, file_name, expires_in_hours=1
            )
            
            # 清理临时文件
            try:
                os.remove(path)
            except OSError as e:
                print(f"警告: 无法删除临时文件 {path}: {e}")
            
            return signed_url
        else:
            # 上传失败，抛出异常
            raise Exception(f"OSS上传失败，状态码: {response.status_code}, 响应: {response.text}")
            
    except FileNotFoundError as e:
        raise e
    except Exception as e:
        # 确保在异常情况下也尝试清理文件
        try:
            if os.path.exists(path):
                os.remove(path)
        except OSError:
            pass
        raise Exception(f"OSS上传过程中发生错误: {str(e)}")
